﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Physis.Client.JsonResponses {

    public class jqGridDataResponse : ResponseBase {

        #region Private Properties

        private Int32 pageSize = 1;

        private Int32 currentPage = 0;


        private String filters = String.Empty; // FILTERS SEARCH FROM JQGRID QUERY STRING

        private String sidx = String.Empty; // SORT COLUMN FROM JQGRID QUERY STRING 

        private String sord = String.Empty; // SORT ORDER FROM JQGRID QUERY STRING 

        #endregion


        #region Public Properties

        // THESE PROPERTIES ARE LOWER CASE ON PURPOSE TO SUPPORT AUTOMATIC SERIALIZATION

        // THE JQGRID IS CASE SENSITIVE TO THE JSON RETURNED

        public Int32 PageSize { get { return ((pageSize < 1) ? 1 : pageSize); } set { pageSize = value; } }

        /// <summary>Total records available in current page based on page size.</summary>
        public Int32 total { get { return (records / PageSize) + (((records % PageSize) == 0) ? 0 : 1); } }

        /// <summary>Current Page</summary>
        public Int32 page { get { return currentPage; } }

        /// <summary>Total records available in the entire data set.</summary>
        public Int32 records { get; set; }

        /// <summary>IEnumerable Data Set</summary>
        public System.Collections.IEnumerable rows { get; set; }

        #endregion


        #region Constructors

        public jqGridDataResponse (System.Collections.Specialized.NameValueCollection queryString) {

            // READ IN REQUEST PARAMETERS FROM QUERY STRING

            Int32.TryParse (queryString["page"], out currentPage);

            Int32.TryParse (queryString["rows"], out pageSize);

            filters = queryString["filters"];

            sidx = queryString["sidx"];

            sord = queryString["sord"];

            return;

        }

        #endregion


        #region Public Methods

        /// <summary>
        /// This methods provides a way to segment a page out of a full collection of data for returning to the application. 
        /// </summary>
        /// <param name="forRows">The original data collection</param>
        public void RowsByPage (System.Collections.IEnumerable forRows) {

            // MOVE TO LIST OF OBJECTS TO MAKE IT MUTABLE

            List<Object> sourceRows = (from currentRow in forRows.Cast<Object> () 
                                       
                                       select currentRow).ToList();


            // FUTURE: APPLY FILTERS (ASSUME ALL AND FILERS), THIS ONLY SUPPORTS THE FILTER TOOLBAR SEARCH 

            // THIS DOES NOT AT THIS TIME SUPPORT THE FULL FILTER/QUERY CAPABILITY OF JQGRID (RESERVED FOR FUTURE)

            if (String.IsNullOrWhiteSpace (filters)) { filters = String.Empty; } // MAKE SURE NOT A NULL STRING

            List<Data.FilterDescriptor> filtersDescriptors = new List<Data.FilterDescriptor> ();

            if (filters.Contains ("\"rules\":[{")) {

                String ruleSet = filters.Substring (filters.IndexOf (":[{") + 3);

                ruleSet = ruleSet.Substring (0, ruleSet.Length - 3);

                List<String> rules = ruleSet.Split (new String[] { "},{" }, StringSplitOptions.None).ToList<String> ();

                foreach (String currentRule in rules) {

                    Data.FilterDescriptor filterDescriptor = new Data.FilterDescriptor ();

                    foreach (String currentProperty in currentRule.Substring (1, currentRule.Length - 2).Split (new String[] { "\",\"" }, StringSplitOptions.None)) {

                        String propertyName = currentProperty.Split (new String[] { "\":\"" }, StringSplitOptions.None)[0];

                        String propertyValue = currentProperty.Split (new String[] { "\":\"" }, StringSplitOptions.None)[1];

                        switch (propertyName.ToLower ()) {

                            case "field": filterDescriptor.PropertyPath = propertyValue; break;

                            case "op": filterDescriptor.Operator = Data.Enumerations.DataFilterOperator.Contains; break; // FUTURE: FIXED VALUE, RESERVED FOR FUTURE USE

                            case "data": filterDescriptor.ParameterValue = propertyValue; break;

                        }

                    }

                    filtersDescriptors.Add (filterDescriptor);
                    
                }

            }

            foreach (Data.FilterDescriptor currentFilterDescriptor in filtersDescriptors) {

                if (sourceRows.Count > 0) { // ONLY NEED TO FILTER WHEN SOURCE ROWS HAS ROWS 

                    if (sourceRows[0].GetType ().GetProperty (currentFilterDescriptor.PropertyPath) != null) { // CHECK TO MAKE SURE THE PROPERTY EXISTS 

                        // FUTURE: THIS SIMPLY DOES A CASE INSENSITIVE CONTAINS TO FIND MATCHING VALUES 

                        sourceRows = (from currentSourceRow in sourceRows

                                      where (currentSourceRow.GetType ().GetProperty (currentFilterDescriptor.PropertyPath).GetValue (currentSourceRow, null).ToString ().ToLower ().Contains (currentFilterDescriptor.ParameterValue.ToString ().ToLower ()))

                                      select currentSourceRow).ToList ();

                    }

                }

            }


            // CHANGE SORT OF ROWS BASED ON INCOMING SELECTIONS (FROM QUERY STRING), OTHERWISE TAKE DEFAULT ORDER 

            if ((!String.IsNullOrWhiteSpace (sidx)) && (sourceRows.Count > 0)) {

                if (sord.ToLower () == "desc") {

                    sourceRows = sourceRows.OrderByDescending (forObject => forObject.GetType ().GetProperty (sidx).GetValue (forObject, null)).ToList ();

                }

                else {

                    sourceRows = sourceRows.OrderBy (forObject => forObject.GetType ().GetProperty (sidx).GetValue (forObject, null)).ToList ();

                }

            }
        
            // PAGE THE AVAILABLE ROWS BASED ON PAGE SIZE 

            rows = (from currentRow in sourceRows

                        select currentRow).Skip ((currentPage - 1) * PageSize).Take (PageSize);

            records = forRows.Cast<Object> ().Count ();

            return;

        }

        #endregion 

    }

}
